package cn.jji8.patronSaint.quick.boxesMenu;

import cn.jji8.patronSaint.GlobalLoader;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.io.IOException;
import java.util.*;

/**
 * 可保存的MenuManger，是menuManger子类，只能保存箱子里的物品，ClickImplement不能保存的。
 * */
public class CanSavedMenuManger extends MenuManger {
    //--------------------------------------------------
    //静态

    //需要保存的CanSavedMenuManger
    static final Set<CanSavedMenuManger> savedSet = new HashSet<>();

    /**
     * 立即保存全部的CanSavedMenuManger，服务器关闭时会调用一次，自动保存时也会调用
     * */
    public static void saveAll(){
        Set<CanSavedMenuManger> savedSet1 = new HashSet<>(savedSet);
        for(CanSavedMenuManger canSavedMenuManger:savedSet1){
            canSavedMenuManger.save();
            savedSet.remove(canSavedMenuManger);
        }
    }
    static{
        //加载自己的自动保存
        //注册自己需要的监听器
        GlobalLoader.onEnablTask(plugin -> {
            Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, CanSavedMenuManger::saveAll,20*30,20*30);
            Bukkit.getPluginManager().registerEvents(new Listener(){
                /**
                 * 监听关闭背包事件
                 * */
                @SuppressWarnings("unused")
                @EventHandler
                public void InventoryCloseEvent(InventoryCloseEvent a){
                    CanSavedMenuManger.InventoryCloseEvent(a);//传递给CanSavedMenuManger
                }
            }, plugin);
        });
        //注册插件关闭时的任务
        GlobalLoader.onDisableTask(plugin -> onDisable());
    }
    private static void onDisable(){
        //关闭全部玩家的背包
        Collection<? extends Player> collection = Bukkit.getOnlinePlayers();
        for(Player player:collection){
            player.closeInventory();
        }
        //保存全部玩家的CanSavedMenuManger数据
        CanSavedMenuManger.saveAll();
    }
    /**
     * 监听关闭背包事件，这个方法不需要你来调用
     * */
    private static void InventoryCloseEvent(InventoryCloseEvent a) {
        MenuManger menuManger = MenuManger.lookForMenuMangerList(a.getInventory());
        if(menuManger!=null){
            if(menuManger instanceof CanSavedMenuManger){
                savedSet.add((CanSavedMenuManger) menuManger);
            }
        }
    }

    //-------------------------------------------------
    //实例

    //文件位置
    final File file;
    //数据文件
    YamlConfiguration yamlConfiguration = null;
    /**
     * 构造器，获取一个菜单管理器，并加载文件，若没有将自动创建，无需手动保存，会自动保存。
     *
     * @param size 箱子的大小，这里是排数
     * @param file 保存的文件位置
     */
    @SuppressWarnings("unused")
    public CanSavedMenuManger(File file, int size) {
        this(file,size,"箱子");
    }

    /**
     * 构造器，获取一个菜单管理器，并加载文件，若没有将自动创建，无需手动保存，会自动保存。
     * 不可以同时存在两个相同路径的CanSavedMenuManger，如果构造一个CanSavedMenuManger请确保上一个相同路径的CanSavedMenuManger已不再被使用。
     *
     * @param size 箱子的大小，这里是排数
     * @param name 箱子的名字。
     * @param file 保存的文件位置
     */
    public CanSavedMenuManger(File file,int size, String name) {
        super(size, name);
        for(CanSavedMenuManger stack:savedSet){
            if(stack.file.equals(file)){
                //让相同路径，但是还没有保存数据的CanSavedMenuManger将数据写入ymal
                stack.readYaml();
                //获取他的ymal
                yamlConfiguration = stack.yamlConfiguration;
                //取消他的保存
                savedSet.remove(stack);
                //让自己即将保存
                savedSet.add(this);
                break;
            }
        }
        this.file = file;
        //加载对应的yml
        if(yamlConfiguration==null){
            yamlConfiguration = YamlConfiguration.loadConfiguration(file);
        }
        //加载箱子里的物品
        for(int i=0;i<size*9;i++){
            ItemStack itemStack = yamlConfiguration.getItemStack("menu."+i);
            if(itemStack!=null){
                menu.setItem(i,itemStack);
            }
        }
    }
    /**
     * 调用父类MenuManger的getMenu()方法获取到Inventory后，如果修改了箱子里的数据，就需要调用此方法，让他在下一个自动保存中会保存。
     * */
    @SuppressWarnings("unused")
    public void needSane(){
        savedSet.add(this);
    }

    /**
     * 立即保存箱子里的数据，一般不需要调用此方法，因为会自动保存。
     * */
    public void save() {
        //将数据写入ymal
        readYaml();

        savedSet.remove(this);
        try {
            yamlConfiguration.save(file);
        } catch (IOException e) {
            e.printStackTrace();
            Bukkit.getLogger().severe("CanSavedMenuManger保存到"+file+"失败！");
        }
    }
    /**
     * 将数据写入yamlConfiguration，但不保存
     * */
    private void readYaml(){
        //保存箱子里的物品
        int size = menu.getSize();
        for(int i=0;i<size;i++){
            yamlConfiguration.set("menu."+i,menu.getItem(i));
        }
        //保存全部的CanSaveClickImplement的数据
        Set<Integer> Intset = SingleClickMap.keySet();
        for(int s:Intset){
            ClickImplement Single = SingleClickMap.get(s);
            if(Single instanceof CanSaveClickImplement){
                YamlConfiguration SingleClickYml = new YamlConfiguration();
                ((CanSaveClickImplement)Single).Save(SingleClickYml);
                String name = ((CanSaveClickImplement)Single).getName();
                if(name==null){
                    name = String.valueOf(s);
                }
                yamlConfiguration.set("Single."+name,SingleClickYml.saveToString());
            }
        }
    }

    /**
     * 为某个格子设置点击事件执行器，如果是CanSaveClickImplement就调用他的load方法加载上次的数据，如果需要删除click可以传null
     * CanSaveClickImplement加载到的yamlConfiguration是格子对应的yamlConfiguration.
     */
    @SuppressWarnings("unused")
    @Override
    public void setSingleClick(int lattice, ClickImplement click) {
        super.setSingleClick(lattice, click);
        if(click==null){
            return;
        }
        if(click instanceof CanSaveClickImplement){
            String name = ((CanSaveClickImplement)click).getName();
            if(name==null){
                name = String.valueOf(lattice);
            }
            String ymlString = yamlConfiguration.getString("Single."+name);
            YamlConfiguration yamlConfiguration = new YamlConfiguration();
            if(ymlString!=null){
                try {
                    yamlConfiguration.loadFromString(ymlString);
                } catch (InvalidConfigurationException e) {
                    e.printStackTrace();
                    Bukkit.getLogger().warning(file+"中的部分数据加载出错！");
                }
            }
            ((CanSaveClickImplement)click).load(yamlConfiguration);
        }
    }


    //下面是只要是修改箱子内容了，就将他添加到需要保存的列表

    /**
     * 清除单个格子.
     */
    @Override
    public void clear(int i) {
        super.clear(i);
        savedSet.add(this);
    }

    /**
     * 清除整个物品栏.
     */
    @Override
    public void clear() {
        super.clear();
        savedSet.add(this);
    }

    /**
     * 向物品栏添加所给的物品堆.本方法将尽可能完美地尝试填充已有的但还未达到堆叠上限的物品堆 和空格子.
     * 返回一个包含无法添加的物品堆的HashMap，键是传入的参数中那一物品堆所在的索引, 值是传入的可变参数中指定索引处的物品堆.如果所有物品都被添加, 将返回一个空的HashMap.
     * <p>
     * 如果传入的物品堆超过了这一物品材质的最大堆叠数量,首先它们将被按照 物品材质的最大堆叠量，添加一部分至堆叠数量未满的物品堆.如果没有堆叠数量未满的物品堆剩余, 物品堆将被以物品栏所允许的物品堆最大堆叠数量分割, 允许超出物品材质的最大堆叠数量.
     * <p>
     * 在对此方法的一些已知实现中，会将传入的物品堆参数的数量设置为超出而无法添加的物品堆数量.
     * <p>
     * 译注:可能指部分实现中，此方法会直接操作传入参数对象，而不是将传入参数对象进行复制后再操作其数量
     *
     */
    @Override
    public @NotNull
    HashMap<Integer, ItemStack> addItem(@NotNull ItemStack... itemStacks) throws IllegalArgumentException {
        @NotNull HashMap<Integer, ItemStack> s = super.addItem(itemStacks);
        savedSet.add(this);
        return s;
    }

    /**
     * 移除在物品栏内的指定物品堆.
     * 本方法将按照你所给的物品堆的数量和种类作为参数尽可能多地移除物品堆.
     * <p>
     * 返回的HashMap包含不能移除的物品堆，map的键 是你所给的参数的索引值，值是在你所给的参数指定索引处的物品堆.如果 所给的物品堆全部被移除，将返回一个空HashMap.
     */
    @Override
    public @NotNull HashMap<Integer, ItemStack> removeItem(@NotNull ItemStack... itemStacks) throws IllegalArgumentException {
        @NotNull HashMap<Integer, ItemStack> s = super.removeItem(itemStacks);
        savedSet.add(this);
        return s;
    }

    /**
     * 移除在物品栏内与给定物品匹配的所有物品堆.
     *
     * @throws IllegalArgumentException - 若material为null
     */
    @Override
    public void remove(@NotNull Material material) throws IllegalArgumentException {
        super.remove(material);
        savedSet.add(this);
    }

    /**
     * 移除在此物品栏内与给定物品堆匹配的所有物品堆.
     * 只有物品堆的种类和数目相应匹配时对应的格子才会被匹配.
     *
     */
    @Override
    public void remove(@NotNull ItemStack itemStack) {
        super.remove(itemStack);
        savedSet.add(this);
    }

    /**
     * 设置在指定索引的物品堆.
     */
    @Override
    public void setItem(int i, @Nullable ItemStack itemStack) {
        super.setItem(i, itemStack);
        savedSet.add(this);
    }

    /**
     * 完全地覆盖物品栏的内容.移除所有存在的内容并替换为指定的物品堆.
     *
     * @param itemStacks 内容的完全替换;数组长度必须小于或等于getSize().
     */
    @Override
    public void setContents(@NotNull ItemStack[] itemStacks) throws IllegalArgumentException {
        super.setContents(itemStacks);
        savedSet.add(this);
    }

    /**
     * 放置所给的物品堆至物品栏.
     *
     * @param itemStacks 作为物品栏内容的物品堆
     * @throws IllegalArgumentException 如果数组容量超越了物品栏的容量
     */
    @Override
    public void setStorageContents(@NotNull ItemStack[] itemStacks) throws IllegalArgumentException {
        super.setStorageContents(itemStacks);
        savedSet.add(this);
    }
}
